//#include "xMappingNode.h"
#include "xMappingTree.h"
//#include "vmLibrary.h"
//#include "xLink.h"
#include <vector>
#include "WOP_Array_Entry.h"
#include "xNodeType_VM.h"
#include "xNodeType_WOP.h"
#include "xNodeType_vmcollector.h"
#include "xNodeType_wopcollector.h"
#include "xNodeType_Math.h"
#include "xNodeType_vmclone.h"
#include "xNodeType_gate.h"
#include "xNodeType_composite.h"
#include "xNodeType_StringComposer.h"
#include "xNodeType_Clock.h"
#include "xNodeType_TM.h"
#include "Factory_xNodeType.h"
#include "xInMappingPort.h"
#include "xOutMappingPort.h"
#include "xLink.h"
#include "Exception.h"

using namespace std ;

const string MDS_VERSION = "3.0" ;
/** this method assign the level by a sweep of the mapping tree */

xMappingTree::xMappingTree() {
}

vector<xMappingNode*>  xMappingTree::getChildNodes(xMappingNode *p) {
	return p->getChildNodes();
}
vector<xMappingNode*>  xMappingTree::getParentNodes(xMappingNode *p) {
	return p->getParentNodes();
}

std::vector< xMappingNode * > xMappingTree::get_output_nodes() const {
	std::vector< xMappingNode * > result;
	for( auto it = this->outputTerminals_.begin(); it != this->outputTerminals_.end(); ++it ) {
		auto p = ( *it );
		if( p->getType() == "gate" || p->getType() == "wop" ) {
			if( p->getParentNodes().empty() ) {
				result.push_back( p );
			}
		}
	}
	return result;
}

int xMappingTree::maxlevel_of_childs(xMappingNode * p) {
	vector<xMappingNode *>::iterator kitr ;
	vector<xMappingNode *> childs ;

	int	max_kidlevel = -1 ;
	childs = p->getChildNodes();
	for (kitr = childs.begin(); kitr != childs.end(); kitr++) {
		xMappingNode* kid = (*kitr);
		if (kid->get_level() > max_kidlevel) 
			max_kidlevel = kid->get_level();
	}
	return max_kidlevel ;
}
void xMappingTree::find_level0_nodes() {
	xMappingNode *p;
	vector<xMappingNode *>::iterator itr ;
	bool has_wop = false ;
	bool has_gate = false ;
	for (itr=inputTerminals_.begin(); itr != inputTerminals_.end();) {
		p = (*itr); 
		if (p->getType()== "wop") {
			assert(p->getChildNodes().size() == 0) ; // a wop node should not have any childrens
			if (p->getParentNodes().size() == 0) {
				this->delete_node(p);				 // this is the case where a wop is not connected to any mapping node
				// FIXME dont erase anything in a for-loop
				itr=inputTerminals_.erase(itr);           // we delete from the mapping tree
				continue;
			} 
			else {
				p->set_level(0); 
				has_wop = true ;
			}
		} 
		else if (p->getType() == "gate") {
			if (p->getChildNodes().size() == 0) {
				p->set_level(0);
				has_gate = true ;
			}
		}
		itr++;
	}
	assert((has_wop && !has_gate) || (!has_wop&& has_gate));  // one case is have wop as level 0 mapping nodes
	// another case is have gates as level 0 mapping nodes (composite
}
/** 
*/
void xMappingTree::find_level1_nodes() {
	xMappingNode *p;
	vector<xMappingNode *>::iterator itr ;
	
	for (itr=_nodes.begin(); itr != _nodes.end() ; itr++) {
		p = (*itr); 
		if (p->getChildNodes().size() == 0 && p->get_level() == -1) 
			p->set_level(1);
	}
}
void xMappingTree::compute_level() {

	xMappingNode *p ;
	vector<xMappingNode *>::iterator itr ;
	vector<xMappingNode *>::iterator kitr ;
	vector<xMappingNode *> childs ;
	vector<xMappingNode *> parents ;

	// reset all the level before assign new level id
	for (itr = _nodes.begin(); itr != _nodes.end();  itr++) {
		(*itr)->set_level(-1);
	}

	_maxlevel = 0 ;

	find_level0_nodes(); // level0 nodes are wops or gates. 
	find_level1_nodes() ; // level1 nodes are those which are not wops or gates but do not have children
	
	bool changed ;
	do {
		changed = false ;
		for (itr=_nodes.begin(); itr != _nodes.end(); itr++) {
			p = (*itr); 
			if (p->get_level() == 0) continue ; // the level 0 nodes are skipped 

			int	maxkid = maxlevel_of_childs(p);  // return the biggest level from kids
			// debugging story here:
			// when a composite node has a linked to a parent node.
			// the parent node actually connect to the root node in the composite
			// so, it may returns a value much bigger than expect.
			// the root node in the composite already has a level id 
			// This can be a bug in the future

			if (maxkid == -1) continue ;  // none of the levels of child nodes of p are determined.
			// in this case, we should not increase p's level into 0
			// Because if it is 0, then it is a terminal nodes.
			if (maxkid >= p->get_level()) {
				assert(maxkid+1 > 0);
				p->set_level(maxkid+1);
				changed = true ;
				if (_maxlevel < maxkid+1 ) _maxlevel = maxkid+1 ;

			}
		}
	} while (changed) ;
	if( this->_maxlevel < 1 ) {
		throw Exception( "this is an empty mapping tree", __FILE__, __LINE__ );
	}
}

vector<xMappingNode *>  xMappingTree::get_nodes_by_level(int level) {
	vector<xMappingNode *>::iterator itr ;
	vector<xMappingNode *> result ;
	// reset all the level before assign new level id
	for (itr = _nodes.begin(); itr != _nodes.end();  itr++) {
		if ((*itr)->get_level() == level) 
			result.push_back((*itr));
	}
	// assert(result.size() != 0);
	return result ;
}

void xMappingTree::update() {
	vector<xMappingNode *>::iterator itr ;
	compute_level();  // compute the level 

	// level 0 should be all WOPs 
	for (int i=0; i<= this->get_max_level(); i++) {
		vector<xMappingNode*> nodes = this->get_nodes_by_level(i);
		for (unsigned int j=0; j< nodes.size(); j++) {
			nodes[j]->getNodeTypePtr()->update(); 
		}
	}

}
/** this method add a child mappingtree to this. The 'attached" is the VVnode that the child mapping supposed to attach.
*/
#if 0
void xMappingTree::add_tree(xMappingTree childmt, xMappingNode *right_node,  int inport_index) {
	
	vector<xMappingNode*> childnodes = childmt.get_nodes();
	vector<xMappingNode *>::iterator itr ;
	for (itr = childnodes.begin(); itr != childnodes.end(); itr++) {
		_nodes.push_back((*itr));
	}
	vector<xLink*> childlinks = childmt.get_links();
	vector<xLink*>::iterator lnkitr ;
	for (lnkitr = childlinks.begin(); lnkitr != childlinks.end(); lnkitr++) {
		_links.push_back((*lnkitr));
	}
	xMappingNode * root = childmt.get_root();
	assert(root != NULL);

	create_link(root, 0, right_node, inport_index);  // when add a tree to a right_node, we usually
	                                                 // connect the first outport (indexed 0)
	this->compute_level();
}
#endif

void xMappingTree::add_tree(xMappingTree* src){
	auto srcChildList = src->get_nodes();
	for( auto it = srcChildList.begin(); it != srcChildList.end(); ++it ) {
		this->_nodes.push_back( *it );
	}

	auto srcLinkList = src->getLinks();
	for( auto it = srcLinkList.begin(); it != srcLinkList.end(); ++it ) {
		this->_links.push_back( *it );
	}
	
	auto srcTerminal = src->get_input_terminals();
	for( auto it = srcTerminal.begin(); it != srcTerminal.end(); ++it ){
		this->inputTerminals_.push_back( *it );
	}

	srcTerminal = src->get_output_terminals();
	for( auto it = srcTerminal.begin(); it != srcTerminal.end(); ++it ) {
		this->outputTerminals_.push_back( *it );
	}
}

xMappingNode * xMappingTree::create_node(xNodeType *nodet) {
	xMappingNode *p = new xMappingNode(nodet);
	_nodes.push_back(p);
	return p ;
}
void xMappingTree::delete_node(xMappingNode *nd) {
	auto it = std::find( this->_nodes.begin(), this->_nodes.end(), nd );
	if( it == this->_nodes.end() ) {
		assert( !"node to be deleted does not exist" );
	}
	this->_nodes.erase( it );
	for (int i = 0; i < nd->getInportSize(); i++) {
		xInMappingPort *inp = nd->getInPort(i);
		xLink *lnk = inp->getLink();
		if(lnk!=NULL)
			this->delete_link(lnk);
				
	}
	for (int i=0; i< nd->getOutportSize();i++) {
		xOutMappingPort *op = nd->getOutPort(i) ;
		delete_links(op);
	}
	// search unfolded children, remove dangling reference
	for( it = this->_nodes.begin(); it != this->_nodes.end(); ++it ) {
		auto p = *it;
		p->deleteUnfoldedChild( nd );
	}
	delete nd;
}
bool xMappingTree::verify_link(xOutMappingPort *left, xInMappingPort *right) {

	xNodeType* left_nt = left->getParent()->getNodeTypePtr();
	xNodeType* right_nt = right->getParent()->getNodeTypePtr() ;
	bool left_ok = left_nt-> verify_rhs_link(left, right, right_nt);	
	bool right_ok = right_nt->verify_lhs_link(left, left_nt, right);
	if (left_ok && right_ok) return true ;
	return false ;

	/*
	//如果已經有一模一樣的link則return false
	if(right->getLink()!=NULL){
		if(right->getLink()->left==left)
			return false;
	}

	if (left->getType() == "wop") {
		xNodeType* nt = left->getParent()->getNodeTypePtr() ;
		if (nt->getType() == "wop") {
			xNodeType_WOP *wopnt = (xNodeType_WOP*)(nt);
			WOP_Entry *wop = wopnt->getWOP();

			if(right->getType() == "int*" && wop->getTypeStr() == "int") return true ;
			if (right->getType() == "bool*" && wop->getTypeStr() == "bool") return true ;
			if (right->getType() == "float*" && (wop->getTypeStr() == "float" ||
												 wop->getTypeStr() == "double")) return true ;
			if (right->getType() == "double*" && (wop->getTypeStr() == "float" ||
												 wop->getTypeStr() == "double")) return true ;
			if (right->getType() == "char*"	&& wop->getTypeStr() == "char") return true ;
			if (right->getType() == "wop" ||
				right->getType() == "tostring*" ||
				right->getType() == "varname" ||
				right->getType() == "string*"
			 )
			return true;
		} else if (nt->getType() == "math") { 
			xNodeType_Math *mathnt = (xNodeType_Math*)(nt);
			WOP_Entry *wop = mathnt->getWOP();
			if(right->getType() == "int*")  return true ;
			if(right->getType() == "float*") return true ;
			if(right->getType() == "double*") return true ;
			if(right->getType() == "char*") return true ; // an integer may be casted into a byte

			if (right->getType() == "wop" || right->getType() == "tostring*") return true;
		}
	} else if(right->getParent()->getType() == "math") {
		if(left->getType() == "int*" ||
			left->getType() == "float*" ||
			left->getType() == "double*" 
			)
			return true;
	}
	else if (right->getType() == "tostring*") {
		if(left->getType() == "int*" ||
			   left->getType() == "bool*" ||
			   left->getType() == "float*" ||
			   left->getType() == "double*" ||
			   left->getType() == "char*" ||
			   left->getType() == "wop" ||
			   left->getType() == "string*" ||
			   left->getType() == "tostring*" 
			   )
			   return true ;
	}
	else if(left->getType() == right->getType())
		return true;

	return false ;
	*/
}

xLink * xMappingTree::create_link(xOutMappingPort *left, xInMappingPort *right) {
	
	// assert(false); // under construction 
	// assert(left->getType() == right->getType()) ;  // an inport has often has types such as vm, wop, int int*, bool, bool*
	// in the future, we should assert false here for illegal link 
	
	//verify_link(left,right);

	xLink *conn = new xLink(left,right);
	left->add_link(conn);
	right->add_link(conn);
	_links.push_back(conn);
	return conn ;
}
xLink *xMappingTree::create_link(xMappingNode *leftnode, int outport_index, xInMappingPort *right) {
		return create_link(leftnode->getOutPort(outport_index), right); 
}

xLink * xMappingTree::create_link(xMappingNode *leftnode, int outportindex, xMappingNode *rightnode, int inportindex) {
	xInMappingPort *inp = rightnode->getInPort(inportindex);
	return create_link(leftnode->getOutPort(outportindex), inp);
}

void xMappingTree::delete_link(xLink *p) {
	auto it = std::remove( this->_links.begin(), this->_links.end(), p );
	bool found = it != this->_links.end();
	this->_links.erase( it, this->_links.end() );
	if(found){
		p->left->remove_link(p); // this is usually an output port, so it may be connected to more than one link
		p->right->remove_link();
		delete p ;
		p = NULL;
	}
}

void xMappingTree::delete_links(xOutMappingPort *op) {
	const auto & links = op->getLinks();
	std::for_each( links.begin(), links.end(), [this]( xLink * p )->void {
		this->delete_link( p );
	} );
}

#if 0
DIVA_VM * xMappingTree::getRootVM() {
	if( !this->isSingleRoot() ) {
		return NULL;
	}
	xNodeType *np = this->roots_.front()->getNodeTypePtr();
	if (np->getType() == "vm") {
		xNodeType_VM *vp = dynamic_cast< xNodeType_VM*>( np );
		assert(vp != NULL) ;
		assert(vp->getVM() != NULL);
		return vp->getVM();
	} else if (np->getType() == "composite") {
		xNodeType_composite *cp = dynamic_cast< xNodeType_composite* >( np );
		assert(cp != NULL);
		xMappingTree * xmt = cp->getxMappingTree();
		return xmt->getRootVM(); // a recursive call the get the root vm
	} else if( np->getType() == "gate" ) {
		xNodeType_VM * vp = dynamic_cast< xNodeType_VM * >( np->getParent()->getInPort( 0 )->getLink()->left->getParent()->getNodeTypePtr() );
		assert(vp != NULL) ;
		assert(vp->getVM() != NULL);
		return vp->getVM();
	} else if( np->getType() == "tm" || np->getType() == "existing" ) {
		return NULL;
	} else {
		// TODO throw exception
		assert( !"This mapping is not a VM" );
	}
	return NULL ;
}
#endif

string xMappingTree::toMDString() {
	
	ostringstream MDSstr ;

	// xMappingNode內的MDSFlag用來記錄xMappingNode是否已經有被寫出成MDS
	// 會這樣做是因為，目前xMappingNode都只負責寫出自己的MDS，但是如果使用+號展開時
	// xMappingNode會需要呼叫child來寫出MDS，所以避免重複寫MDS，使用MDSFlag來記錄
	// 故，每一次寫出MDS時需要再將這些flags reset
	this->resetMDSFlag();		
	vector<xMappingNode *>::iterator itr ;
	compute_level();  // compute the level 

	MDSstr << " #MDS version " << MDS_VERSION << endl ;

	MDSstr << " node_definition (" << endl ;

	// print level 0 nodes' mds string 
	vector<xMappingNode*>wopMappingNodes = get_nodes_by_level(0);
	for ( int i = 0; i < wopMappingNodes.size(); ++i )
	{
		if ( wopMappingNodes[i]->getMDSFlag() == false &&
			 wopMappingNodes[i]->isForeachElements() == false ){
			MDSstr <<  " " <<  wopMappingNodes[i]->toMDString(xNodeType::NormalMDSFormat) ;
		}
	}

	for (int i=1; i<= get_max_level(); i++) {
		vector<xMappingNode*> nodes = get_nodes_by_level(i);
		for (unsigned int j=0; j< nodes.size(); j++) {

			// if node is not for each array element, write node information in node_definition block
			if ( !nodes[j]->isForeachElements() ){
				MDSstr <<  " " <<  nodes[j]->toMDString(xNodeType::NormalMDSFormat) ;
				// if a node is a VM node, it could create a VM object
				// The behaviors of the createMappedObject() depends on the type of node
			}
		}
	}
	MDSstr << " )" << endl ;

	MDSstr << " terminal_nodes ( " ;
	vector<xMappingNode*> terminals = get_input_terminals();
	for (itr = terminals.begin(); itr != terminals.end(); itr++) {
		if ( !(*itr)->isForeachElements() ){
			xMappingNode *p = (*itr);
			MDSstr << " " << p->getSerialNo() ;
		}
	}
	MDSstr << " )" << endl ;

	MDSstr << " output_terminals ( ";
	for( auto it = this->outputTerminals_.begin(); it != this->outputTerminals_.end(); ++it ) {
		if( !( *it )->isForeachElements() ) {
			MDSstr << " " << ( *it )->getSerialNo();
		}
	}
	MDSstr << " )" << endl;


	MDSstr << " link_definition (" << endl ;
	vector<xLink*>::iterator ktr ;
	for (ktr = _links.begin(); ktr != _links.end(); ktr++) {
		if ( !(*ktr)->left->getParent()->isForeachElements() &&  
			 !(*ktr)->right->getParent()->isForeachElements())
		{
			xLink *lnk = (*ktr);
			MDSstr << "  " << lnk->toMDString(xLink::NormalMDSFormat);
		}
	}
	MDSstr << " ) " << endl ;


	MDSstr << " parameterized (" << endl ;
	
	std::string terminalType = this->inputTerminals_[0]->getNodeTypePtr()->getType();
	
	// 假如 type 是 xNodeType_gate，則跳過 parameterized (目前這個 block 只 for mapping dialog 使用)
	if ( terminalType == "wop")
	{
		int index = this->inputTerminals_.size()-1;
		xNodeType_WOP* nodeType = dynamic_cast<xNodeType_WOP*>(this->inputTerminals_[index]->getNodeTypePtr());
		assert(nodeType);

		// if EntryItem is in foreach area, write mds in parameterized block
		if ( nodeType->getParent()->isForeachElements() ){

			WOP_Array_Entry* wop= dynamic_cast<WOP_Array_Entry*>(nodeType->getWOP());
			assert(wop);

			int dimension =  wop->getDimension();
			int indent = 0;
			vector<int> dimensionRange = wop->getDimensionRange();
			char arrayIndex = 'i';

			for ( int i = 0; i < dimension; ++i )
			{
				MDSstr << printIndent(indent) <<"FORLOOP " << "$" << arrayIndex << " " << 0 << " " << dimensionRange[i]-1 << endl;
				arrayIndex++;
				indent++;
			}


			for (int i=0; i<= get_max_level(); i++) {
				// add nodes
				vector<xMappingNode*> nodes = get_nodes_by_level(i);
				for (unsigned int j=0; j< nodes.size(); j++) {

					// if node is not for each array element, write node information in node_definition block
					if ( nodes[j]->isForeachElements() &&
						 nodes[j]->getParentNodes().size() != 0){

						MDSstr << printIndent(indent)  <<  nodes[j]->toMDString(xNodeType::ParameterizedMDSFormat);
						// if a node is a VM node, it could create a VM object
						// The behaviors of the createMappedObject() depends on the type of node
					}
				}
			}
			// add links 
			for (ktr = _links.begin(); ktr != _links.end(); ktr++) {
				if ( (*ktr)->left->getParent()->isForeachElements() ||
					(*ktr)->right->getParent()->isForeachElements())
				{
					xLink *lnk = (*ktr);
					MDSstr << printIndent(indent) << lnk->toMDString(xLink::ParameterizedMDSFormat);
				}
			}

			// add terminals
			for (itr = terminals.begin(); itr != terminals.end(); itr++) {
				if ( (*itr)->isForeachElements() &&
					 (*itr)->getParentNodes().size()!=0){
					xMappingNode *p = (*itr);
					MDSstr << printIndent(indent) << "ADD_TERMINAL" <<" " << "SN" <<p->getSerialNo() << "\n";
				}
			}

			for ( int i = 0; i < dimension; ++i )
			{
				indent--;
				MDSstr << printIndent(indent) << "ENDLOOP"  << endl;
			}
		}
	}
	MDSstr << " )" << endl ;

	string whole = MDSstr.str();
	return whole ;
}
string xMappingTree::toCompositeDEString(string idname, string type, string description) {

	ostringstream ostr ;
	ostr << "#DES version 2.2" << endl ;
	ostr << "IDNAME = " << idname << endl ;
	ostr << "CATEGORY = " << type << endl ;
	ostr << "TYPE = dontcare " << endl ;

	ostr << "SECTION description {" << endl ;
	ostr << description << endl ;
	ostr << "}" << endl ;

	
	ostr << "SECTION parameters {" << endl ;
	compute_level();

	vector<xMappingNode*> level0_nodes = this->get_nodes_by_level(0);
	assert(level0_nodes.size() >= 1);

	//xNodeType *nt = level0_nodes[0]->getNodeTypePtr();
	//nt->setName("radius");
	for (unsigned int j=0; j< level0_nodes.size(); j++) {
		xNodeType *nt = level0_nodes[j]->getNodeTypePtr();
		assert(nt->getType() == "gate");
		
		xInMappingPort *inp = level0_nodes[j]->getInPort(0);
		ostr << "port " << j << " = " << inp->getType() << " " 
			 << inp->getName() << " " 
			 << CLASS_to_string(inp->getClass())  << " " 
			 << inp->getDefaultValue() << " "
			 << CATEGORY_to_string(inp->getCategory()) << endl;
		assert(inp->getDefaultValue() != "");
		
	}

	ostr << "}" << endl ;

	ostr << "SECTION compositebody {" << endl ;
	ostr << toMDString() ;
	ostr << "}" << endl ;
	ostr << "END" << endl ;

	return ostr.str() ;
}

std::string xMappingTree::toTMString( const std::string & valType, const std::string & type, const std::string & description ) {
	ostringstream ostr ;
	ostr << "#DES version 2.2" << endl ;
	ostr << "IDNAME = " << valType << endl ;
	ostr << "CATEGORY = " << type << endl ;
	ostr << "TYPE = " << valType << endl ;

	ostr << "SECTION description {" << endl ;
	ostr << description << endl ;
	ostr << "}" << endl ;

	
	ostr << "SECTION parameters {" << endl ;
	compute_level();

	vector<xMappingNode*> level0_nodes = this->get_nodes_by_level(0);
	assert(level0_nodes.size() >= 1);

	//xNodeType *nt = level0_nodes[0]->getNodeTypePtr();
	//nt->setName("radius");
	for (unsigned int j=0; j< level0_nodes.size(); j++) {
		xNodeType *nt = level0_nodes[j]->getNodeTypePtr();
		assert(nt->getType() == "wop");

		ostr << "port " << j << " = " << nt->getType() << " " 
			 << nt->getName() << " " 
			 << "default" << " " 
			 << "NULL" << " "
			 << "OTHER" << endl;
		
	}

	ostr << "}" << endl ;

	ostr << "SECTION compositebody {" << endl ;
	ostr << toMDString() ;
	ostr << "}" << endl ;
	ostr << "END" << endl ;

	return ostr.str() ;
}

/* these two methods are defined in command_agent.h */
void Tokenize(const string& str,
                      vector<string>& tokens,
                      const string& delimiters = " ");
string combine_tokens(vector<string> tokens);

xMappingTree* xMappingTree::fromMDStoXMappingTree(string MDS, WOP_Entry *en) {
	xMappingTree* xmt = new xMappingTree();
	map<string, xMappingNode *> xnodes ;
	map<string, xMappingNode*>::iterator kitr ;

	vector<string> tokens;
    Tokenize(MDS, tokens, "\n");
	string str = combine_tokens(tokens);
	tokens.clear();
	Tokenize(str, tokens," ");

	assert(tokens[0] == "#MDS");
	assert(tokens[1] == "version");
	assert(tokens[2] == MDS_VERSION);
	assert(tokens[3] == "node_definition");
	assert(tokens[4] == "(");
	unsigned int i = 5 ; 
	unsigned int w = 0 ;

	// read all the node definition
	do {	
		assert(tokens[i] == "node");
		string serialno = tokens[i+1] ;
		string nodetype = tokens[i+2] ; // nodetypes are "wop" "vm" "vmcollector"
		string parameter1 = tokens[i+3] ; // parameter 1 of an wop is vartype()
		string parameter2 = tokens[i+4] ; // parameter 2 of an wop is its shortname. 
		if (nodetype == "wop") {
			assert(en != NULL);
			// now, we search all the children of wop en and return the matched one
			WOP_Entry *wop = en->findMatchedWOP(parameter1, parameter2) ; 
			// where parameter1 is vartype, parameter2 is shortname of wop entry
			assert(wop != NULL);
			xNodeType *wp = (xNodeType *) new xNodeType_WOP(wop);
			xMappingNode *wnode = xmt->create_node(wp);
			int usedtokens = wp->parseMDString(tokens,i) ;
			i += usedtokens ;
			xnodes[serialno] = wnode ;
			w++ ;
		} else if (nodetype == "vm") {
			istringstream buf(parameter1);
			string idname ;
			buf >> idname ;
			
			istringstream buf2(parameter2);
			int inp_no ;
			buf2 >> inp_no ;

			xNodeType_VM *vm = new xNodeType_VM(idname);
			xMappingNode *vmnode = xmt->create_node((xNodeType*)vm); // the default string begins at i+5
			int usedtokens = vm->parseMDString(tokens,i) ;
			i += usedtokens ;
			xnodes[serialno] = vmnode ;
		} else if (nodetype == "vmcollector") {
			//xNodeType *vmcollector_nt = (xNodeType *) new xNodeType_vmcollector();
			xNodeType *vmcollector_nt = (xNodeType*) new xNodeType_vmcollector();
			xMappingNode *vmcollector = xmt->create_node(vmcollector_nt);
			int usedtokens = vmcollector_nt->parseMDString(tokens,i);
			i += usedtokens ;
			xnodes[serialno] = vmcollector ;
		} else if (nodetype == "wopcollector") {
			//xNodeType *wopcollector_nt = (xNodeType *) new xNodeType_wopcollector();
			xNodeType *wopcollector_nt = (xNodeType*) new xNodeType_wopcollector();
			xMappingNode *wopcollector = xmt->create_node(wopcollector_nt);
			int usedtokens = wopcollector_nt->parseMDString(tokens,i);
			i += usedtokens ;
			xnodes[serialno] = wopcollector ;
		} else if (nodetype == "math") {
			xNodeType_Math *am = new xNodeType_Math();
			xNodeType *nt = (xNodeType*) am ;
			xMappingNode *amnode = xmt->create_node(nt);
			int usedtokens = am->parseMDString(tokens,i) ;
			i += usedtokens ;
			xnodes[serialno] = amnode ;
		} else if (nodetype == "stringcomposer"){
			xNodeType_StringComposer *scm = new xNodeType_StringComposer();
			xNodeType *nt = (xNodeType*) scm;
			xMappingNode *amnode = xmt->create_node(nt);
			int usedtokens = scm->parseMDString(tokens,i) ;
			i += usedtokens ;
			xnodes[serialno] = amnode ;
		} else if(nodetype == "clock"){
			xNodeType_Clock *cm = new xNodeType_Clock();
			xNodeType *nt = (xNodeType*) cm;
			xMappingNode *amnode = xmt->create_node(nt);
			int usedtokens = cm->parseMDString(tokens,i) ;
			i += usedtokens ;
			xnodes[serialno] = amnode ;
		} else if (nodetype == "gate") {
			xNodeType *gate = (xNodeType *)new xNodeType_gate(parameter1);
			xMappingNode *amnode = xmt->create_node(gate);
			int usedtokens = gate->parseMDString(tokens,i);
			i += usedtokens ;
			xnodes[serialno] = amnode ;
		} else if (nodetype == "vmclone") {
			xNodeType *vmclone_nt = (xNodeType *) new xNodeType_vmclone();
			xMappingNode *amnode = xmt->create_node(vmclone_nt);
			int usedtokens = vmclone_nt->parseMDString(tokens,i);
			i += usedtokens ;
			xnodes[serialno] = amnode ;
		} else if (nodetype == "composite") {
			xNodeType *composite_nt = (xNodeType*) new xNodeType_composite(parameter1);
			xMappingNode *cnode = xmt->create_node(composite_nt);
			int usedtokens = composite_nt->parseMDString(tokens,i) ;
			i += usedtokens ;
			xnodes[serialno] = cnode ;
		} else 
			assert(false); 
		
	
	} while (tokens[i] != ")");

	i++ ;
	// now parepare to read the terminal definition
	assert(tokens[i] == "terminal_nodes"); i++ ;
	assert(tokens[i] == "("); i++ ;
	do {
		string serialno = tokens[i] ; // get the right mapping node
		kitr = xnodes.find(serialno);
		assert(kitr != xnodes.end());
		xMappingNode *terminal = kitr->second ;
		xmt->add_input_terminal(terminal);
		i++ ;
	} while (tokens[i] != ")"); 

	++i;
	assert( tokens[i] == "output_terminals" );
	++i;
	assert( tokens[i] == "(" );
	++i;
	do {
		string serialno = tokens[i] ; // get the right mapping node
		kitr = xnodes.find(serialno);
		assert(kitr != xnodes.end());
		xMappingNode *terminal = kitr->second ;
		xmt->add_output_terminal(terminal);
		++i;
	} while( tokens[i] != ")" );

	i++ ;
	// now prepare to read the link definition
	assert(tokens[i] == "link_definition"); i++ ;
	assert(tokens[i] == "("); i++ ;
	do {
		assert(i+7 < tokens.size());
		assert(tokens[i] == "node") ;
	
		string leftserialno = tokens[i+1] ;	// get the left mapping node
		kitr = xnodes.find(leftserialno);
		assert(kitr != xnodes.end());
		xMappingNode *left = kitr->second ;

		assert(tokens[i+2] == "port");
		istringstream obuf(tokens[i+3]);
		int outportid ;
		obuf >> outportid ;

		assert(tokens[i+4] == "to");
		assert(tokens[i+5] == "node");

		string rightserialno = tokens[i+6] ; // get the right mapping node
		kitr = xnodes.find(rightserialno);
		assert(kitr != xnodes.end());
		xMappingNode *right = kitr->second ;

		assert(tokens[i+7] == "port");  
		istringstream buf(tokens[i+8]);// get the port index 
		int portid ;
		buf >> portid ;

		// some special cases like vmcollector and wopcollector which have dynamic number of inports
		// need to be handled specially

		if (right->getNodeTypePtr()->getType() == "vm_collector") {
			xInMappingPort *newinp = right->newInPort("vmport","vm", CLASS_REQUIRED, "NULL", CATEGORY_OTHER);
			xmt->create_link(left,outportid, newinp);
		} else if (right->getNodeTypePtr()->getType() == "wopcollector"||
					right->getNodeTypePtr()->getType() == "math") {
			xInMappingPort *newinp = right->newInPort("wopport","wop", CLASS_REQUIRED, "NULL", CATEGORY_OTHER);
			xmt->create_link(left,outportid, newinp);
		} else if (right->getNodeTypePtr()->getType() == "string_composer") {
				xInMappingPort *newinp = right->newInPort("wopport","tostring*", CLASS_REQUIRED, "NULL", CATEGORY_OTHER);
				xmt->create_link(left,outportid, newinp);
		}
		else 
			xmt->create_link(left,outportid, right->getInPort(portid));

		i += 9 ;
	} while (tokens[i] != ")");
	
	return xmt ;
}
void xMappingTree::saveToDESfile(string filename,string desstring,bool isModified) {
	
	if(isModified==true){
		//如果isModified=true，則表示xmnLib中有這個vm，否則就利用進入index，而assert(false)
		xMappingNodeTypeLibrary::getInstance()->get_xmnt_descriptor_index(filename);
	}

	const string DES_FILE_PATH = "DES\\";
	string path = DES_FILE_PATH + filename+".des" ;

	//製作新的des file
	ofstream desfile(path.c_str(), ios_base::out | ios_base::trunc);
	assert(desfile); // if file open error, quit for now
	desfile << desstring << endl ;
	desfile << "END" << endl ;
	desfile.close();
	
	//如果只是修改則不增加path到compositeVM.des中
	if(isModified==true)
		return;

	//將des file的路徑加到compositeVMS.des中
	path = DES_FILE_PATH + "CompositeVMs.des";
	fstream filepos(path.c_str(),ios_base::in|ios_base::out);
	
	char current;
	int pos;
	int checknumber=0;
	while(!filepos.eof()){
		filepos>>current;
		if(current=='}'){
			pos=filepos.tellg();
			break;	
		}
	}

	filepos.seekp(pos-1);
	filepos<<"idname = ";
	filepos<<filename;
	filepos<<" path = ";
	filepos<<"DES/"<<filename<<".des"<<"\n"<<"}";
	filepos.close();

}

void xMappingTree::add_input_terminal( xMappingNode *p ) {
	this->inputTerminals_.push_back( p );
}

void xMappingTree::delete_input_terminal( xMappingNode *p ) {
	auto it = remove( this->inputTerminals_.begin(), this->inputTerminals_.end(), p );
	this->inputTerminals_.erase( it, this->inputTerminals_.end() );
}

void xMappingTree::add_output_terminal( xMappingNode * p ) {
	this->outputTerminals_.push_back( p );
}

void xMappingTree::delete_output_terminal( xMappingNode * p ) {
	auto it = remove( this->outputTerminals_.begin(), this->outputTerminals_.end(), p );
	this->outputTerminals_.erase( it, this->outputTerminals_.end() );
}

const std::vector< xMappingNode * > & xMappingTree::get_input_terminals() const {
	return this->inputTerminals_;
}

const std::vector< xMappingNode * > & xMappingTree::get_output_terminals() const {
	return this->outputTerminals_;
}

std::string xMappingTree::printIndent( int indent )
{
	string indentTab;
	for ( int i = 0; i < indent; ++i )
	{
		indentTab += "\t";
	}
	return indentTab;
}

void xMappingTree::clearUnusedWOP()
{
	xMappingNode *p;
	vector<xMappingNode *>::iterator itr ;

	for (itr= this->inputTerminals_.begin(); itr != this->inputTerminals_.end();) {
		p = (*itr); 
		if (p->getType()== "wop") {
			assert(p->getChildNodes().size() == 0) ; // a wop node should not have any childrens
			if (p->getParentNodes().size() == 0)  {
				this->delete_node(p);
				itr=this->inputTerminals_.erase(itr);;
				continue;
			}
		} 
		itr++;
	}
}

void xMappingTree::resetMDSFlag()
{
	std::vector<xMappingNode*>::iterator itr;
	for ( itr = _nodes.begin(); itr != _nodes.end(); ++itr )
	{
		(*itr)->setMDSFlag(false);
	}
}

const std::vector< std::pair< WOP_Entry *, DIVA_VM * > > & xMappingTree::get_wop_vm_pairs() const {
	return this->wop_vm_pairs_;
}

DIVA_VM * xMappingTree::get_vm_by_gate( xMappingNode * node ) const {
	auto it = this->gate_vm_table_.find( node );
	if( it == this->gate_vm_table_.end() ) {
		return nullptr;
	}
	return it->second;
}

// must call after createMappedObject
void xMappingTree::collect_wop_vm_pairs() {
	// collect composite VM
	std::for_each( this->_nodes.begin(), this->_nodes.end(), []( xMappingNode * node )->void {
		if( node->getType() == "composite" ) {
			auto cType = dynamic_cast< xNodeType_composite * >( node->getNodeTypePtr() );
			auto tree = cType->getxMappingTree();
			tree->collect_wop_vm_pairs();
		}
	} );

	// collect wop-vm pairs
	this->wop_vm_pairs_.clear();
	this->gate_vm_table_.clear();
	for( auto it = this->_nodes.begin(); it != this->_nodes.end(); ++it ) {
		auto p = ( *it );
		if( p->getType() == "wop" ) {
			// WOP has exactly one out port
			auto ols = p->getOutPort( 0 )->getLinks();
			for( auto it = ols.begin(); it != ols.end(); ++it ) {
				auto port = ( *it )->right;
				if( port->getType() != "varname" ) {
					continue;
				}
				auto node = port->getParent();
				if( node->getType() == "vm" ) {
					// an ubvm
					auto wopType = dynamic_cast< xNodeType_WOP * >( p->getNodeTypePtr() );
					auto vmType = dynamic_cast< xNodeType_VM * >( node->getNodeTypePtr() );
					this->wop_vm_pairs_.push_back( std::make_pair( wopType->getWOP(), vmType->getVM() ) );
				} else if( node->getType() == "gate" ) {
					// a composite vm
					auto wopType = dynamic_cast< xNodeType_WOP * >( p->getNodeTypePtr() );
					auto cType = dynamic_cast< xNodeType_composite * >( node->getCompositeParent()->getNodeTypePtr() );
					auto subTree = cType->getxMappingTree();
					auto vm = subTree->get_vm_by_gate( node );
					this->wop_vm_pairs_.push_back( std::make_pair( wopType->getWOP(), vm ) );
				} else {
					// ??
				}
			}
		} else if( p->getType() == "gate" ) {
			// gate has exactly one out port
			auto ols = p->getOutPort( 0 )->getLinks();
			for( auto it = ols.begin(); it != ols.end(); ++it ) {
				auto port = ( *it )->right;
				if( port->getType() != "varname" ) {
					continue;
				}
				auto node = port->getParent();
				if( node->getType() == "vm" ) {
					// an ubvm
					auto vmType = dynamic_cast< xNodeType_VM * >( node->getNodeTypePtr() );
					this->gate_vm_table_.insert( std::make_pair( p, vmType->getVM() ) );
				} else if( node->getType() == "gate" ) {
					// a composite vm
					auto cType = dynamic_cast< xNodeType_composite * >( node->getCompositeParent()->getNodeTypePtr() );
					auto subTree = cType->getxMappingTree();
					auto vm = subTree->get_vm_by_gate( node );
					this->gate_vm_table_.insert( std::make_pair( p, vm ) );
				} else {
					// ??
				}
			}
		}
	}
}
